using System;
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;


namespace ValidationFramework.Specifications.Tests
{
	public class HasCountInRangeSpecTests
	{
		public class MyCollection : ICollection<int>
		{
			public void CopyTo(Array array, int index)
			{
				throw new NotImplementedException();
			}

			public void Add(int item)
			{
				throw new NotImplementedException();
			}

			public void Clear()
			{
				throw new NotImplementedException();
			}

			public bool Contains(int item)
			{
				throw new NotImplementedException();
			}

			public void CopyTo(int[] array, int arrayIndex)
			{
				throw new NotImplementedException();
			}

			public bool Remove(int item)
			{
				throw new NotImplementedException();
			}

			public int Count
			{
				get
				{
					return 5;
				}
			}

			public bool IsReadOnly
			{
				get
				{
					throw new NotImplementedException();
				}
			}

			IEnumerator<int> IEnumerable<int>.GetEnumerator()
			{
				throw new NotImplementedException();
			}

			public IEnumerator GetEnumerator()
			{
				throw new NotImplementedException();
			}
		}

		const int expectedMinimum = 2;
		const int expectedMaximum = 4;

		[TestFixture]
		public class Contructor1
		{
			[Test]
			public void Should_Default_CountDuplicates_To_False()
			{
				var validator = new HasCountInRangeSpec(expectedMinimum, expectedMaximum);
				Assert.AreEqual(expectedMinimum, validator.Minimum);
				Assert.AreEqual(expectedMaximum, validator.Maximum);
				Assert.IsFalse(validator.CountDuplicates);
			}
			[Test]
			public void Should_Default_Comparer_To_Null()
			{
				var validator = new HasCountInRangeSpec(expectedMinimum, expectedMaximum);
				Assert.AreEqual(expectedMinimum, validator.Minimum);
				Assert.AreEqual(expectedMaximum, validator.Maximum);
				Assert.IsNull(validator.Comparer);
			}

			[Test]
			[ExpectedException(typeof(ArgumentException), ExpectedMessage = "Minimum must be less than or equal to Maximum.\r\nParameter name: minimum")]
			public void Should_Throw_When_Min_Greater_Than_Max()
			{
				new HasCountInRangeSpec(2, 1);
			}

			[Test]
			public void Should_Allow_Equal_Min_And_Max()
			{
				new HasCountInRangeSpec(1, 1);
			}

		}

		[TestFixture]
		public class IsEquivalentTo
		{
			[Test]
			public void Should_Be_Equivalent_When_Constructed_With_Same_Parameters()
			{
				var validator1 = new HasCountInRangeSpec(10, 15);
				var validator2 = new HasCountInRangeSpec(10, 15);
				Assert.IsTrue(validator1.IsEquivalentTo(validator2));
			}
			[Test]
			public void Should_Be_Equivalent_With_Different_ExcludeDuplicate_Flags()
			{
				var validator1 = new HasCountInRangeSpec(10, 15);
				var validator2 = new HasCountInRangeSpec(10, 15);

				validator2.CountDuplicates = false;

				Assert.IsTrue(validator1.IsEquivalentTo(validator2));
			}
			[Test]
			public void Should_NOT_Be_Equivalent_With_Different_Maximums()
			{
				var validator1 = new HasCountInRangeSpec(10, 15);
				var validator2 = new HasCountInRangeSpec(10, 20);
				Assert.IsFalse(validator1.IsEquivalentTo(validator2));
			}
			[Test]
			public void Should_NOT_Be_Equivalent_With_Different_Minimums()
			{
				var validator1 = new HasCountInRangeSpec(10, 15);
				var validator2 = new HasCountInRangeSpec(5, 15);
				Assert.IsFalse(validator1.IsEquivalentTo(validator2));
			}
			[Test]
			public void Should_NOT_Be_Equivalent_With_Different_Comparers()
			{
				var validator1 = new HasCountInRangeSpec(10, 15);
				var validator2 = new HasCountInRangeSpec(10, 15);
				validator2.Comparer = StringComparer.Ordinal;
				Assert.IsFalse(validator1.IsEquivalentTo(validator2));
			}

		}

		[TestFixture]
		public class Validate
		{
			[Test]
			public void Should_Be_Valid_If_Testing_Null_Value()
			{
				var validator = new HasCountInRangeSpec(2, 4);

				Assert.IsTrue(validator.IsSatisfiedBy(null));
			}

			[Test]
			public void ValidateGenericExcludeDuplicates()
			{
				// Arrange
				var validator = new HasCountInRangeSpec(2, 4);

				var list = new List<int>();

				// Act/Assert
				Assert.IsFalse(validator.IsSatisfiedBy(list));

				list.Add(1);
				Assert.IsFalse(validator.IsSatisfiedBy(list));

				list.Add(1);
				Assert.IsFalse(validator.IsSatisfiedBy(list));

				list.Add(1);
				Assert.IsFalse(validator.IsSatisfiedBy(list));

				list.Add(2);
				Assert.IsTrue(validator.IsSatisfiedBy(list));

				list.Add(3);
				Assert.IsTrue(validator.IsSatisfiedBy(list));

				list.Add(4);
				Assert.IsTrue(validator.IsSatisfiedBy(list));

				list.Add(5);
				Assert.IsFalse(validator.IsSatisfiedBy(list));
			}


#if (!SILVERLIGHT)
			[Test]
			public void Should_Include_Duplicates_When_CountDuplicates_Flag_Is_Set()
			{
				// Arrange
				var validator = new HasCountInRangeSpec(2, 4)
				{
					CountDuplicates = true
				};

				var list = new List<int>();

				list.Add(1);
				Assert.IsFalse(validator.IsSatisfiedBy(list));

				list.Add(1);
				Assert.IsTrue(validator.IsSatisfiedBy(list));

				list.Add(1);
				Assert.IsTrue(validator.IsSatisfiedBy(list));

				list.Add(2);
				Assert.IsTrue(validator.IsSatisfiedBy(list));

				list.Add(3);
				Assert.IsFalse(validator.IsSatisfiedBy(list));

			}

			[Test]
			public void ValidateNonGenericIncludeDuplicates()
			{
				var validator = new HasCountInRangeSpec(2, 4)
				{
					CountDuplicates = true
				};

				var list = new ArrayList();
				Assert.IsFalse(validator.IsSatisfiedBy(list));

				list.Add(1);
				Assert.IsFalse(validator.IsSatisfiedBy(list));

				list.Add(1);
				Assert.IsTrue(validator.IsSatisfiedBy(list));

				list.Add(1);
				Assert.IsTrue(validator.IsSatisfiedBy(list));

				list.Add(1);
				Assert.IsTrue(validator.IsSatisfiedBy(list));

				list.Add(1);
				Assert.IsFalse(validator.IsSatisfiedBy(list));
			}
			[Test]
			public void ValidateNonGenericExcludeDuplicates()
			{
				// Arrange
				var validator = new HasCountInRangeSpec(2, 4)
				{
					CountDuplicates = false
				};

				// Act/Assert
				var list = new ArrayList();
				Assert.IsFalse(validator.IsSatisfiedBy(list));

				list.Add(1);
				Assert.IsFalse(validator.IsSatisfiedBy(list));

				list.Add(1);
				Assert.IsFalse(validator.IsSatisfiedBy(list));

				list.Add(1);
				Assert.IsFalse(validator.IsSatisfiedBy(list));

				list.Add(2);
				Assert.IsTrue(validator.IsSatisfiedBy(list));

				list.Add(3);
				Assert.IsTrue(validator.IsSatisfiedBy(list));

				list.Add(4);
				Assert.IsTrue(validator.IsSatisfiedBy(list));

				list.Add(5);
				Assert.IsFalse(validator.IsSatisfiedBy(list));
			}
#endif
		}




		[TestFixture]
		public class CheckTypes
		{

			[Test]
			public void Should_Be_Able_To_Validate_The_Following_Types()
			{
				var validator = new HasCountInRangeSpec(expectedMinimum, expectedMaximum);
				Assert.IsTrue(validator.CanBeSatisfiedBy(typeof(IList<int>)));
				Assert.IsTrue(validator.CanBeSatisfiedBy(typeof(List<int>)));
				Assert.IsTrue(validator.CanBeSatisfiedBy(typeof(ICollection<int>)));
				Assert.IsTrue(validator.CanBeSatisfiedBy(typeof(LinkedList<int>)));
				Assert.IsTrue(validator.CanBeSatisfiedBy(typeof(Stack<int>)));
#if (!SILVERLIGHT)
				Assert.IsTrue(validator.CanBeSatisfiedBy(typeof(ArrayList)));
#endif
				Assert.IsTrue(validator.CanBeSatisfiedBy(typeof(int[])));

			}


			[Test]
			//[ExpectedException(typeof(ArgumentException), ExpectedMessage = "Property must be a ICollection<T> or ICollection to be used for the LengthCollectionRule.")]
			public void Should_Not_Be_Able_To_Validate_The_Following_Types()
			{
				var validator = new HasCountInRangeSpec(expectedMinimum, expectedMaximum);
				Assert.IsFalse(validator.CanBeSatisfiedBy(typeof(int)));
			}
		}


	}
}